package com.wlyuan.index.domain.source.assembler;

import com.wlyuan.core.domain.valueobj.TenantId;
import com.wlyuan.core.domain.valueobj.UserId;
import com.wlyuan.core.marker.NamedUtils;
import com.wlyuan.core.marker.ValueUtils;
import com.wlyuan.core.named.TrackType;
import com.wlyuan.index.api.dto.combine.CombineIndexDTO;
import com.wlyuan.index.domain.source.Combine;
import com.wlyuan.index.domain.source.utils.SourceUtils;
import com.wlyuan.index.domain.source.valueobj.Character;
import com.wlyuan.index.domain.source.valueobj.*;
import com.wlyuan.transport.dto.CombinePriceDetailDTO;
import com.wlyuan.transport.dto.query.*;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.var;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

import java.util.ArrayList;
import java.util.List;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class CombineAssembler {
    @Mapper(uses = {ValueUtils.class, NamedUtils.class, SourceUtils.class})
    public interface Assembler {

        @Mapping(target = "device", ignore = true)
        @Mapping(target = "trackType", source = "source.device.trackType")
        @Mapping(target = "trackNo", source = "source.device.deviceCode")
        @Mapping(target = "remarks", source = "source.desc")
        @Mapping(target = "statistics", source = "source")
        @Mapping(target = "close.tenantId", source = "source.close.closeTenantId")
        @Mapping(target = "endBill", source = "source.billDayStatus")
        Combine assemble(CombineIndexDTO source, List<Combine.Relation> relations);

        @Mapping(target = "name", source = "customerName")
        @Mapping(target = "type", source = "customerType")
        @Mapping(target = "highName", source = "showCustomerName")
        @Mapping(target = "highType", source = "showCustomerType")
        @Mapping(target = "tenantId", source = "customerTenantId")
        Customer assembleCustomer(CombineCustomerDTO source);

        @Mapping(target = "name", source = "carrierName")
        @Mapping(target = "type", source = "carrierType")
        @Mapping(target = "tenantId", source = "carrierTenantId")
        @Mapping(target = "contactMobile", source = "carrierContactMobile")
        @Mapping(target = "contactName", source = "carrierContactName")
        @Mapping(target = "abbr", source = "carrierAbbr")
        Carrier assembleCarrier(CombineCarrierDTO source);

        @Mapping(target = "minimumFee", source = "source.minCost")
        Quote assembleQuote(CombinePriceDetailDTO source, Integer transportMode);

        @Mapping(target = "differenceValue", source = "valueAddition")
        @Mapping(target = "differenceWeight", source = "weightAddition")
        @Mapping(target = "differenceVolume", source = "volumeAddition")
        Statistics assembleStatistics(CombineIndexDTO source);

        Combine.Device assembleDevice(DeviceDetailDTO source);

        @Mapping(target = "id", source = "userId")
        UserId assembleUserId(UserDTO source);
    }

    private static final Assembler ASSEMBLER = Mappers.getMapper(Assembler.class);


    public static void addExceptions(Combine.Relation relation, CombineIndexDTO source) {
        // FIXME: 旧索引没有这个值，先不处理，后续优化再加上
//        relation.addException(ExceptionStatus.SIGN, source.getSignExceptionQuantity());
//        relation.addException(ExceptionStatus.RECEIPT, source.getReceiptExceptionQuantity());
    }

    private static void addRelation(List<Combine.Relation> relations, CombineIndexDTO source) {
        var status = CombineStatus.valueOf(source.getStatus(), Character.CUSTOMER);
        var relation = new Combine.Relation(Character.CUSTOMER, status, new TenantId(source.getTenantId()), false);
        addExceptions(relation, source);
        relations.add(relation);
    }

    private static void addRelation(List<Combine.Relation> relations, CombineIndexDTO source, CarrierDTO carrier) {
        if (null == carrier || !Carrier.online(carrier.getCarrierType())) {
            return;
        }

        var status = CombineStatus.valueOf(source.getStatus(), Character.CARRIER);
        var relation = new Combine.Relation(Character.CARRIER, status, new TenantId(carrier.getCarrierTenantId()), false);
        addExceptions(relation, source);
        relations.add(relation);
    }

    private static void addDevice(Combine combine, CombineIndexDTO source) {
        if (null == source.getDevice() || !TrackType.isDevice(source.getDevice().getTrackType())) {
            return;
        }
        combine.addDevice(ASSEMBLER.assembleDevice(source.getDevice()));
    }

    private static void addQuote(Combine combine, CombineIndexDTO source) {
        if (null == source.getPrice()) {
            return;
        }
        var quote = ASSEMBLER.assembleQuote(source.getPrice(), null);
        combine.addQuote(quote);
    }

    public static Combine assemble(CombineIndexDTO source) {
        var relations = new ArrayList<Combine.Relation>();
        addRelation(relations, source);
        addRelation(relations, source, source.getCarrier());
        var combine = ASSEMBLER.assemble(source, relations);
        addDevice(combine, source);
        addQuote(combine, source);
        return combine;
    }
}
